﻿using System;
using System.Collections.Generic;
using System.Globalization;
using Medianamik.Core.Urls;

namespace Aron.Weiler
{
    /// <summary>
    /// Multi-Key Dictionary Class
    /// http://www.aronweiler.com/2009/02/multi-key-generic-dictionary-class-for.html
    /// </summary>
    /// <typeparam name="IUrlDictionaryEntry">Value Type</typeparam>
    /// <typeparam name="String">Primary Key Type</typeparam>
    /// <typeparam name="String">Sub Key Type</typeparam>
    //public class MultiKeyDictionary<String, String, IUrlDictionaryEntry>
    public class MultiKeyDictionary : Dictionary<String, IUrlDictionaryEntry>
    {
        internal readonly Dictionary<MultiKeyDictionaryKey, String> subDictionary = new Dictionary<MultiKeyDictionaryKey, String>();
        internal readonly Dictionary<String, MultiKeyDictionaryKey> primaryToSubkeyMapping;

        readonly object lockObject = new object();

        public MultiKeyDictionary(StringComparer stringComparer)
            : base(stringComparer)
        {
            primaryToSubkeyMapping = new Dictionary<String, MultiKeyDictionaryKey>(stringComparer);
        }

        public IUrlDictionaryEntry this[MultiKeyDictionaryKey subKey]
        {
            get
            {
                IUrlDictionaryEntry item;
                if (TryGetValue(subKey, out item))
                    return item;

                throw new KeyNotFoundException("sub key not found: " + subKey.ToString());
            }
        }

        public void Associate(MultiKeyDictionaryKey subKey, String primaryKey)
        {
            lock (lockObject)
            {
                if (!ContainsKey(primaryKey))
                    throw new KeyNotFoundException(string.Format("The base dictionary does not contain the key '{0}'", primaryKey));

                subDictionary[subKey] = primaryKey;
                primaryToSubkeyMapping[primaryKey] = subKey;
            }
        }

        public bool TryGetValue(MultiKeyDictionaryKey subKey, out IUrlDictionaryEntry val)
        {
            val = default(IUrlDictionaryEntry);

            lock (lockObject)
            {
                String ep;
                if (subDictionary.TryGetValue(subKey, out ep))
                {
                    if (!TryGetValue(ep, out val))
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }

            return true;
        }

        public bool ContainsKey(MultiKeyDictionaryKey subKey)
        {
            IUrlDictionaryEntry val;

            return TryGetValue(subKey, out val);
        }

        public void Remove(MultiKeyDictionaryKey subKey)
        {
            lock (lockObject)
            {
                Remove(subDictionary[subKey]);

                primaryToSubkeyMapping.Remove(subDictionary[subKey]);
                subDictionary.Remove(subKey);
            }
        }

        public void Add(String primaryKey, MultiKeyDictionaryKey subKey, IUrlDictionaryEntry val)
        {
            lock (lockObject)
                Add(primaryKey, val);

            Associate(subKey, primaryKey);
        }

        public IUrlDictionaryEntry[] CloneValues()
        {
            lock (lockObject)
            {
                IUrlDictionaryEntry[] values = new IUrlDictionaryEntry[Values.Count];

                Values.CopyTo(values, 0);

                return values;
            }
        }

        public String[] ClonePrimaryKeys()
        {
            lock (lockObject)
            {
                String[] values = new String[Keys.Count];

                Keys.CopyTo(values, 0);

                return values;
            }
        }

        public MultiKeyDictionaryKey[] CloneSubKeys()
        {
            lock (lockObject)
            {
                MultiKeyDictionaryKey[] values = new MultiKeyDictionaryKey[subDictionary.Keys.Count];

                subDictionary.Keys.CopyTo(values, 0);

                return values;
            }
        }

        public IEnumerator<KeyValuePair<String, IUrlDictionaryEntry>> GetEnumerator()
        {
            lock (lockObject) return GetEnumerator();
        }
    }

    public struct MultiKeyDictionaryKey
    {
        public readonly Guid NodeId;
        public readonly CultureInfo Culture;

        public MultiKeyDictionaryKey(Guid nodeId, CultureInfo culture)
        {
            NodeId = nodeId;
            Culture = culture;
        }
    }
}